package concurrecy.atomic;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

interface ICounter {
    void increment();
    int get();
}

interface ICasCounter extends ICounter {
    int getFailCount();
}

/**
 * 线程不安全的计数器
 */
class Counter implements ICounter {
    private int value;
    @Override
    public void increment() {
        value ++;
    }

    @Override
    public int get() {
        return value;
    }
}

/**
 * 使用CAS实现原子计数器类。
 */
class AtomicCounter implements ICasCounter {
    private int value;
    private AtomicInteger failCount = new AtomicInteger(0);

    private static Unsafe unsafe;
    private static long valueOffset;

    // 初始化 unsafe 和 valueOffset
    static {
        try {
            Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafeField.setAccessible(true);
            unsafe = (Unsafe) theUnsafeField.get(null);
            valueOffset = unsafe.objectFieldOffset(AtomicCounter.class.getDeclaredField("value"));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void increment() {
        while(!unsafe.compareAndSwapInt(this, valueOffset, value, value + 1))
//            failCount.incrementAndGet();
            ;
    }

    @Override
    public int get() {
        return value;
    }

    @Override
    public int getFailCount() {
        return failCount.get();
    }
}

/**
 * 使用CAS volatile 实现原子计数器类。
 */
class AtomicVolatileCounter implements ICasCounter {
    private volatile int value;
    private AtomicInteger failCount = new AtomicInteger(0);

    private static Unsafe unsafe;
    private static long valueOffset;

    // 初始化 unsafe 和 valueOffset
    static {
        try {
            Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafeField.setAccessible(true);
            unsafe = (Unsafe) theUnsafeField.get(null);
            valueOffset = unsafe.objectFieldOffset(AtomicVolatileCounter.class.getDeclaredField("value"));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void increment() {
        while(!unsafe.compareAndSwapInt(this, valueOffset, value, value + 1))
//            failCount.incrementAndGet();
            ;
    }

    @Override
    public int get() {
        return value;
    }

    @Override
    public int getFailCount() {
        return failCount.get();
    }
}

/**
 * 计数器测试
 */
public class CounterTest {
    // 测试次数
    static final int N = 1000;
    // 线程数
    static final int P = 8;
    // 每个线程累加多少次
    static final int C = 100000;
    // 遇到错误停止
    static final boolean ERROR_STOP = true;
    // 打印结果
    static final boolean PRINT_RESULT = false;

    static ExecutorService executor = Executors.newCachedThreadPool();

    static void test(ICounter counter) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(P);
        for (int i = 0; i < P; i++) {
            executor.execute(() -> {
                for (int j = 0; j < C; j++) {
                    counter.increment();
                }
                latch.countDown();
            });
        }
        latch.await();
        int value = counter.get();
        // 判断结果是否正确
        if(value == C * P) {
            if(PRINT_RESULT)
                System.out.println(value);
        } else {
            if(PRINT_RESULT)
                System.err.println(value);
            if(ERROR_STOP)
                throw new RuntimeException("error");
        }
    }

    static long testCas(ICasCounter casCounter) throws InterruptedException {
        long point = System.currentTimeMillis();
        test(casCounter);
        return System.currentTimeMillis() - point;
    }

    public static void main(String[] args) throws InterruptedException {
        int useTime = 0;
        for (int i = 0; i < N; i++) {
            ICasCounter counter = new AtomicCounter();
//            ICasCounter counter = new AtomicVolatileCounter();
            long t;
            try {
                t = testCas(counter);
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
            useTime += t;
            System.out.printf("%d - %d\n", i, t);
        }
        System.out.println("use time avg -> " + useTime / N / P);
        executor.shutdown();
    }
}
